`timescale 1ps/1ps
module RAM16K (RDATA, RCLK, RCLKE, RE, RADDR, WCLK, WCLKE, WE, WADDR, MASK, WDATA);
output [15:0] RDATA;
input RCLK;
input RCLKE;
input RE;
input [9:0] RADDR;
input WCLK;
input WCLKE;
input WE;
input [9:0] WADDR;
input [15:0] MASK;
input [15:0] WDATA;

parameter INIT_0 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_1 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_2 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_3 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_4 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_5 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_6 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_7 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_8 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_9 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_F = 256'h0000000000000000000000000000000000000000000000000000000000000000;

parameter INIT_10 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_11 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_12 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_13 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_14 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_15 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_16 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_17 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_18 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_19 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_1A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_1B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_1C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_1D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_1E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_1F = 256'h0000000000000000000000000000000000000000000000000000000000000000;

parameter INIT_20 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_21 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_22 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_23 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_24 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_25 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_26 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_27 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_28 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_29 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_2A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_2B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_2C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_2D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_2E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_2F = 256'h0000000000000000000000000000000000000000000000000000000000000000;

parameter INIT_30 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_31 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_32 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_33 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_34 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_35 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_36 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_37 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_38 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_39 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_3A = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_3B = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_3C = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_3D = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_3E = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_3F = 256'h0000000000000000000000000000000000000000000000000000000000000000;

// local Parameters
localparam			CLOCK_PERIOD = 200;	//
localparam 			DELAY	= (CLOCK_PERIOD/10);		// Clock-to-output delay. Zero
							// time delays can be confusing
							// and sometimes cause problems.
localparam 			BUS_WIDTH = 16;		// Width of RAM (number of bits)

localparam 			ADDRESS_BUS_SIZE = 10;	// Number of bits required to
							// represent the RAM address

localparam   ADDRESSABLE_SPACE  = 2**ADDRESS_BUS_SIZE;	// Decimal address range [2^Size:0]


// SIGNAL DECLARATIONS
wire			   	WCLK_g, RCLK_g;
reg 				WCLKE_sync, RCLKE_sync; 
assign (weak0, weak1) RCLKE =1'b1 ;
assign (weak0, weak1) RE =1'b0 ;
assign (weak0, weak1) WCLKE =1'b1 ;
assign (weak0, weak1) WE =1'b0 ;

assign (weak0, weak1) MASK = 16'b0;

//reg  [BUS_WIDTH-1:0] Memory [ADDRESSABLE_SPACE-1:0];	// The RAM
reg	Memory	[BUS_WIDTH*ADDRESSABLE_SPACE-1:0];
// 
event Read_e, Write_e;

//////////////////// Collision detect begins here ///////////////////////////////
localparam 	TRUE = 1'b1;
localparam	FALSE = 1'b0;
reg 		Time_Collision_Detected = 1'b0;
wire		Address_Collision_Detected;

event Collision_e;

time COLLISION_TIME_WINDOW = (CLOCK_PERIOD/8); // This is an arbitray value, but is better than using an absolute 
						    // value, because the actual time window depends on the actual silicon 
						    // implementation. Thus the test is indicative of an Error and not
						    // guaranteed to be an error. Even so this is usefull.
time time_WCLK_RCLK, time_WCLK, time_RCLK;


//function reg Check_Timed_Window_Violation;
function	Check_Timed_Window_Violation;	//	by Jeffrey
input T1, T2, Minimum_Time_Window;
time T1, T2;
time Minimum_Time_Window;
time Difference;	
	begin
		Difference = (T1 - T2);
		if (Difference < 0) Difference = -Difference;
		Check_Timed_Window_Violation = (Difference < Minimum_Time_Window);
	end
endfunction


initial begin
       time_WCLK = CLOCK_PERIOD;	// Arbitrary initialisation value, ensure no window collison error on first clock edge.
       time_RCLK = (CLOCK_PERIOD*8);	// Arbitrary initialisation difference value, ensure no collision error on first clock edge.					
end

integer	i,j;

genvar k;
wire [9:0] RADDR_g;
wire [9:0] WADDR_g;
wire [15:0] WDATA_g;
for (k = 0; k < 10; k = k + 1) begin
	assign RADDR_g[k] = (RADDR[k] === 1'bz)? 1'b0 : RADDR[k];
	assign WADDR_g[k] = (WADDR[k] === 1'bz)? 1'b0 : WADDR[k];
	assign WDATA_g[k] = (WDATA[k] === 1'bz)? 1'b0 : WDATA[k];
	assign WDATA_g[k+6] = (WDATA[k+6] === 1'bz)? 1'b0 : WDATA[k+6];
end

initial	//	initialize ram16k by init parameters, section by section
begin
	for	(i=0; i<=256/BUS_WIDTH -1; i=i+1)
	begin
		for	(j=0; j<=BUS_WIDTH-1; j=j+1)
		begin 

			Memory[BUS_WIDTH*i+j]		=	INIT_0[BUS_WIDTH*i+j];
			Memory[256*1+BUS_WIDTH*i+j]	=	INIT_1[BUS_WIDTH*i+j];
			Memory[256*2+BUS_WIDTH*i+j]	=	INIT_2[BUS_WIDTH*i+j];
			Memory[256*3+BUS_WIDTH*i+j]	=	INIT_3[BUS_WIDTH*i+j];
			Memory[256*4+BUS_WIDTH*i+j]	=	INIT_4[BUS_WIDTH*i+j];
			Memory[256*5+BUS_WIDTH*i+j]	=	INIT_5[BUS_WIDTH*i+j];
			Memory[256*6+BUS_WIDTH*i+j]	=	INIT_6[BUS_WIDTH*i+j];
			Memory[256*7+BUS_WIDTH*i+j]	=	INIT_7[BUS_WIDTH*i+j];
			Memory[256*8+BUS_WIDTH*i+j]	=	INIT_8[BUS_WIDTH*i+j];
			Memory[256*9+BUS_WIDTH*i+j]	=	INIT_9[BUS_WIDTH*i+j];
			Memory[256*10+BUS_WIDTH*i+j]	=	INIT_A[BUS_WIDTH*i+j];
			Memory[256*11+BUS_WIDTH*i+j]	=	INIT_B[BUS_WIDTH*i+j];
			Memory[256*12+BUS_WIDTH*i+j]	=	INIT_C[BUS_WIDTH*i+j];
			Memory[256*13+BUS_WIDTH*i+j]	=	INIT_D[BUS_WIDTH*i+j];
			Memory[256*14+BUS_WIDTH*i+j]	=	INIT_E[BUS_WIDTH*i+j];
			Memory[256*15+BUS_WIDTH*i+j]	=	INIT_F[BUS_WIDTH*i+j];

			Memory[256*16+BUS_WIDTH*i+j]	=	INIT_10[BUS_WIDTH*i+j];
			Memory[256*17+BUS_WIDTH*i+j]	=	INIT_11[BUS_WIDTH*i+j];
			Memory[256*18+BUS_WIDTH*i+j]	=	INIT_12[BUS_WIDTH*i+j];
			Memory[256*19+BUS_WIDTH*i+j]	=	INIT_13[BUS_WIDTH*i+j];
			Memory[256*20+BUS_WIDTH*i+j]	=	INIT_14[BUS_WIDTH*i+j];
			Memory[256*21+BUS_WIDTH*i+j]	=	INIT_15[BUS_WIDTH*i+j];
			Memory[256*22+BUS_WIDTH*i+j]	=	INIT_16[BUS_WIDTH*i+j];
			Memory[256*23+BUS_WIDTH*i+j]	=	INIT_17[BUS_WIDTH*i+j];
			Memory[256*24+BUS_WIDTH*i+j]	=	INIT_18[BUS_WIDTH*i+j];
			Memory[256*25+BUS_WIDTH*i+j]	=	INIT_19[BUS_WIDTH*i+j];
			Memory[256*26+BUS_WIDTH*i+j]	=	INIT_1A[BUS_WIDTH*i+j];
			Memory[256*27+BUS_WIDTH*i+j]	=	INIT_1B[BUS_WIDTH*i+j];
			Memory[256*28+BUS_WIDTH*i+j]	=	INIT_1C[BUS_WIDTH*i+j];
			Memory[256*29+BUS_WIDTH*i+j]	=	INIT_1D[BUS_WIDTH*i+j];
			Memory[256*30+BUS_WIDTH*i+j]	=	INIT_1E[BUS_WIDTH*i+j];
			Memory[256*31+BUS_WIDTH*i+j]	=	INIT_1F[BUS_WIDTH*i+j];

			Memory[256*32+BUS_WIDTH*i+j]	=	INIT_20[BUS_WIDTH*i+j];
			Memory[256*33+BUS_WIDTH*i+j]	=	INIT_21[BUS_WIDTH*i+j];
			Memory[256*34+BUS_WIDTH*i+j]	=	INIT_22[BUS_WIDTH*i+j];
			Memory[256*35+BUS_WIDTH*i+j]	=	INIT_23[BUS_WIDTH*i+j];
			Memory[256*36+BUS_WIDTH*i+j]	=	INIT_24[BUS_WIDTH*i+j];
			Memory[256*37+BUS_WIDTH*i+j]	=	INIT_25[BUS_WIDTH*i+j];
			Memory[256*38+BUS_WIDTH*i+j]	=	INIT_26[BUS_WIDTH*i+j];
			Memory[256*39+BUS_WIDTH*i+j]	=	INIT_27[BUS_WIDTH*i+j];
			Memory[256*40+BUS_WIDTH*i+j]	=	INIT_28[BUS_WIDTH*i+j];
			Memory[256*41+BUS_WIDTH*i+j]	=	INIT_29[BUS_WIDTH*i+j];
			Memory[256*42+BUS_WIDTH*i+j]	=	INIT_2A[BUS_WIDTH*i+j];
			Memory[256*43+BUS_WIDTH*i+j]	=	INIT_2B[BUS_WIDTH*i+j];
			Memory[256*44+BUS_WIDTH*i+j]	=	INIT_2C[BUS_WIDTH*i+j];
			Memory[256*45+BUS_WIDTH*i+j]	=	INIT_2D[BUS_WIDTH*i+j];
			Memory[256*46+BUS_WIDTH*i+j]	=	INIT_2E[BUS_WIDTH*i+j];
			Memory[256*47+BUS_WIDTH*i+j]	=	INIT_2F[BUS_WIDTH*i+j];

			Memory[256*48+BUS_WIDTH*i+j]	=	INIT_30[BUS_WIDTH*i+j];
			Memory[256*49+BUS_WIDTH*i+j]	=	INIT_31[BUS_WIDTH*i+j];
			Memory[256*50+BUS_WIDTH*i+j]	=	INIT_32[BUS_WIDTH*i+j];
			Memory[256*51+BUS_WIDTH*i+j]	=	INIT_33[BUS_WIDTH*i+j];
			Memory[256*52+BUS_WIDTH*i+j]	=	INIT_34[BUS_WIDTH*i+j];
			Memory[256*53+BUS_WIDTH*i+j]	=	INIT_35[BUS_WIDTH*i+j];
			Memory[256*54+BUS_WIDTH*i+j]	=	INIT_36[BUS_WIDTH*i+j];
			Memory[256*55+BUS_WIDTH*i+j]	=	INIT_37[BUS_WIDTH*i+j];
			Memory[256*56+BUS_WIDTH*i+j]	=	INIT_38[BUS_WIDTH*i+j];
			Memory[256*57+BUS_WIDTH*i+j]	=	INIT_39[BUS_WIDTH*i+j];
			Memory[256*58+BUS_WIDTH*i+j]	=	INIT_3A[BUS_WIDTH*i+j];
			Memory[256*59+BUS_WIDTH*i+j]	=	INIT_3B[BUS_WIDTH*i+j];
			Memory[256*60+BUS_WIDTH*i+j]	=	INIT_3C[BUS_WIDTH*i+j];
			Memory[256*61+BUS_WIDTH*i+j]	=	INIT_3D[BUS_WIDTH*i+j];
			Memory[256*62+BUS_WIDTH*i+j]	=	INIT_3E[BUS_WIDTH*i+j];
			Memory[256*63+BUS_WIDTH*i+j]	=	INIT_3F[BUS_WIDTH*i+j];
		end 
	end
end

assign Address_Collision_Detected = ((RE & WE & WCLKE & RCLKE)&(WADDR == RADDR)); 

always @(WCLK or WCLKE) 
begin 
	if(~WCLK)
	WCLKE_sync = WCLKE;   	
end 

always @(RCLK or RCLKE) 
begin 
	if (~RCLK)
	RCLKE_sync = RCLKE; 	
end 

assign WCLK_g = WCLK & WCLKE_sync;
assign RCLK_g = RCLK & RCLKE_sync;

always @(posedge WCLK_g) begin
	time_WCLK = $time;
end

always @(posedge RCLK_g) begin
    	time_RCLK = $time;
end
integer	RAM16K_RDATA_log_file;									//.....................
initial	RAM16K_RDATA_log_file=("RAM16K_RDATA_log_file.txt");	//.....................
always @(posedge WCLK_g) begin

	Time_Collision_Detected = Check_Timed_Window_Violation(time_WCLK,time_RCLK,COLLISION_TIME_WINDOW);
        if (Time_Collision_Detected & Address_Collision_Detected)begin
        	$display("Warning: Write-Read collision detected, Data read value is XXXX\n");
 		$display("WCLK Time: %.3f   RCLK Time:%.3f  ",time_WCLK, time_RCLK,"WADDR: %d   RADDR:%d\n",WADDR, RADDR); 
 		$fdisplay(RAM16K_RDATA_log_file,"Warning: Write-Read collision detected, Data read value is XXXX\n");
		$fdisplay(RAM16K_RDATA_log_file,"WCLK Time: %.3f   RCLK Time:%.3f  ",time_WCLK, time_RCLK, "WADDR: %d   RADDR:%d\n",WADDR, RADDR); 	
 		-> Collision_e;
	end
end

//	code modify for universal verilog compiler

always @ (posedge WCLK_g)
begin
	if	(WE)
	begin
		-> Write_e;
		for	(i=0;i<=BUS_WIDTH-1; i=i+1)
		begin
			if	(MASK[i] !=1)
				Memory[WADDR_g*BUS_WIDTH+i]	<=	WDATA_g[i];
			else
				Memory[WADDR_g*BUS_WIDTH+i]	<=	Memory[WADDR_g*BUS_WIDTH+i];
		end
	end
end

reg	[BUS_WIDTH-1:0]	RDATA = 0;

// Look at the rising edge of the clock

always @ (posedge RCLK_g)
begin
	if	(RE)
	begin
		-> Read_e;
		if	(Time_Collision_Detected & Address_Collision_Detected) 
			RDATA <= {BUS_WIDTH{1'hX}};
		else
			for	(i=0;i<=BUS_WIDTH-1;i=i+1)
				RDATA[i]	<= Memory[RADDR_g*BUS_WIDTH+i];
	end
end


endmodule	 //	RAM16K
